package com.interview.http;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

public class HttpServer {
    public static void main(String[] args) throws IOException {
        //创建ServerSocketChannel,监听8080端口
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(8080));
        //设置阻塞模式
        ssc.configureBlocking(false);

        //为ssc注册选择器
        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        //创建处理器
        while (true){
            //等待请求,每次请求间隔3s,超过3秒后线程继续向下运行,如果传入0或者不传参数将一直阻塞
            if(selector.select(3000) == 0){
                continue;
            }

            //获取待处理的SelectionKey
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()){
                SelectionKey key = keyIterator.next();
                //启动新线程处理SelectionKey
                new Thread(new HttpHandler(key)).run();
                keyIterator.remove();
            }
        }
    }

    private static class HttpHandler implements Runnable{
        private int bufferSize = 1024;
        private String localCharset = "UTF-8";
        private SelectionKey key;

        public HttpHandler(SelectionKey key){
            this.key = key;
        }
        public void handleAccept() throws IOException {
            SocketChannel clientChannel = ((ServerSocketChannel)key.channel()).accept();
            clientChannel.configureBlocking(false);
            clientChannel.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
        }
        public void handleRead() throws IOException {
            //获取channel
            SocketChannel sc = (SocketChannel) key.channel();
            //获取buffer并重置
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();
            //没有读到内容则关闭
            if(sc.read(buffer) == -1){
                sc.close();
            }else{
                //接收请求数据
                buffer.flip();
                String receviedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
                //控制台打印请求报文
                String[] requestMessages = receviedString.split("\r\n");
                for(String s: requestMessages){
                    System.out.println(s);
                    if(s.isEmpty()){
                        break;
                    }
                }
                //打印控制台信息
                String[] fristLine = requestMessages[0].split(" ");
                System.out.println();
                System.out.println("Method:\t"+fristLine[0]);
                System.out.println("url:\t"+fristLine[1]);
                System.out.println("HTTP Version:\t"+fristLine[2]);
                System.out.println();

                //返回客户端
                StringBuilder sendString = new StringBuilder();
                sendString.append("HTTP/1.1 200 OK\r\n");//响应报文首行,200表示处理成功
                sendString.append("Content-Type:text/html;charset="+localCharset+"\r\n");
                sendString.append("\r\n");//报文结束后加一个空行

                sendString.append("<html><head><title>显示报文</title></head><body>");
                sendString.append("接收到请求报文是:<br/>");
                for(String s: requestMessages){
                    sendString.append(s+"<br>");
                }
                sendString.append("</body></html>");
                buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
                sc.write(buffer);
                sc.close();
            }
        }
        @Override
        public void run(){
            //接收到连接请求时

                try {
                    if(key.isAcceptable()){
                        handleAccept();
                    }
                    //读数据
                    if(key.isReadable()){
                        handleRead();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}
